import difflib
import json
import yaml
import chardet
from jinja2 import Environment, FileSystemLoader  

import re
import sys, os
root_path = os.path.abspath(os.path.join(os.path.abspath(__file__), "../.."))
if root_path not in sys.path:
    sys.path.append(root_path)

from llm_api.chat_messages import ChatMessages

def can_parse_json(response):
    try:
        json.loads(response)
        return True
    except:
        return False

def fix_pseudo_json(json_block):
    """
    尝试将伪JSON（属性名无引号或单引号）修正为标准JSON（属性名双引号），并移除注释
    """
    import re
    
    # 移除行尾注释和块注释
    json_block = re.sub(r'//.*', '', json_block)
    json_block = re.sub(r'/\*.*?\*/', '', json_block, flags=re.DOTALL)
    
    # 1. 属性名无引号 {name: ...} => {"name": ...}
    json_block = re.sub(r'([,{\s])(\w+)\s*:', r'\1"\2":', json_block)
    
    # 2. 属性名单引号 {'name': ...} => {"name": ...}
    json_block = re.sub(r"'([\w_]+)'\s*:", r'"\1":', json_block)
    
    # 3. 属性值单引号 'xxx' => "xxx" (但要注意转义)
    # 先处理不包含转义的单引号字符串
    json_block = re.sub(r':\s*\'([^\'\\]*)\'', r': "\1"', json_block)
    
    # 4. 处理数组中的单引号字符串
    json_block = re.sub(r'\[\s*\'([^\'\\]*)\'', r'["\1"', json_block)
    json_block = re.sub(r'\'([^\'\\]*)\'\s*\]', r'"\1"]', json_block)
    json_block = re.sub(r'\'([^\'\\]*)\'\s*,', r'"\1",', json_block)
    
    # 5. 移除多余逗号
    json_block = re.sub(r',\s*}', '}', json_block)
    json_block = re.sub(r',\s*]', ']', json_block)
    
    # 6. 处理可能的数字键（如 "1": [1, 2]）
    json_block = re.sub(r'([,{\s])(\d+)\s*:', r'\1"\2":', json_block)
    
    # 7. 处理可能的布尔值和null值
    json_block = re.sub(r':\s*true\s*([,}])', r': true\1', json_block)
    json_block = re.sub(r':\s*false\s*([,}])', r': false\1', json_block)
    json_block = re.sub(r':\s*null\s*([,}])', r': null\1', json_block)
    
    return json_block


def match_first_json_block(response):
    import re
    
    # 首先尝试直接解析整个响应
    if can_parse_json(response):
        return response
    
    # 尝试查找JSON代码块
    pattern = r"(?<=[\r\n])```json(.*?)```(?=[\r\n])"
    matches = re.findall(pattern, '\n' + response + '\n', re.DOTALL)
    if not matches:
        pattern = r"(?<=[\r\n])```(.*?)```(?=[\r\n])"
        matches = re.findall(pattern, '\n' + response + '\n', re.DOTALL)
    
    if matches:
        json_block = matches[0]
        # 移除注释和多余内容
        json_block = re.sub(r'//.*', '', json_block)
        json_block = json_block.strip()
        # 移除多余的换行符和空格
        json_block = re.sub(r'\s+', ' ', json_block)
        try:
            json.loads(json_block)
            return json_block
        except json.JSONDecodeError as e:
            # 尝试移除换行符
            json_block_clean = json_block.replace('\r\n', '').replace('\n', '')
            try:
                json.loads(json_block_clean)
                return json_block_clean
            except json.JSONDecodeError as e2:
                # 尝试修正伪JSON
                try:
                    fixed_json = fix_pseudo_json(json_block)
                    json.loads(fixed_json)
                    return fixed_json
                except Exception as e3:
                    error_msg = f"无法解析JSON代码块: 原始错误: {str(e)}, 清理后错误: {str(e2)}, 修正后错误: {str(e3)}\n请确保输出严格为标准JSON格式，属性名和字符串都用双引号，且无注释。"
                    raise Exception(error_msg)
    
    # 如果没有找到代码块，尝试从响应中提取JSON对象
    # 查找可能的JSON对象模式
    json_patterns = [
        r'\{[^{}]*(?:\{[^{}]*\}[^{}]*)*\}',  # 匹配嵌套的JSON对象
        r'\{[^{}]*\}',  # 匹配简单JSON对象
    ]
    
    for pattern in json_patterns:
        matches = re.findall(pattern, response, re.DOTALL)
        for match in matches:
            try:
                # 清理匹配的内容
                cleaned_match = re.sub(r'//.*', '', match)  # 移除注释
                cleaned_match = cleaned_match.strip()
                
                # 尝试解析
                json.loads(cleaned_match)
                return cleaned_match
            except json.JSONDecodeError:
                # 尝试修正伪JSON
                try:
                    fixed_json = fix_pseudo_json(cleaned_match)
                    json.loads(fixed_json)
                    return fixed_json
                except Exception:
                    continue
    
    # 如果还是没找到，尝试从响应中提取看起来像JSON的部分
    lines = response.split('\n')
    json_lines = []
    in_json = False
    
    for line in lines:
        line = line.strip()
        if line.startswith('{') or line.startswith('"') or line.startswith('['):
            in_json = True
        if in_json:
            json_lines.append(line)
        if line.endswith('}') or line.endswith(']'):
            in_json = False
    
    if json_lines:
        potential_json = '\n'.join(json_lines)
        try:
            # 清理并尝试解析
            cleaned_json = re.sub(r'//.*', '', potential_json)
            json.loads(cleaned_json)
            return cleaned_json
        except json.JSONDecodeError:
            try:
                fixed_json = fix_pseudo_json(cleaned_json)
                json.loads(fixed_json)
                return fixed_json
            except Exception:
                pass
    
    raise Exception(f"没有匹配到JSON代码块，请确保输出包含标准JSON代码块。")
    
def parse_first_json_block(response_msgs: ChatMessages):
    assert response_msgs[-1]['role'] == 'assistant'
    return json.loads(match_first_json_block(response_msgs[-1]['content']))

def match_code_block(response):
    response = re.sub(r'\r\n', r'\n', response)
    response = re.sub(r'\r', r'\n', response)
    pattern = r"```(?:\S*\s)(.*?)```"
    matches = re.findall(pattern, response + '```', re.DOTALL)
    return matches

def json_dumps(json_object):
    return json.dumps(json_object, ensure_ascii=False, indent=1)

def parse_chunks_by_separators(string, separators):
    separator_pattern = r"^\s*###\s*(" + "|".join(separators) + r")\s*\n"

    chunks = re.split(separator_pattern, string, flags=re.MULTILINE)

    ret = {}

    current_title = None
    
    for i, chunk in enumerate(chunks):
        if i % 2 == 1: 
            current_title = chunk.strip()
            ret[current_title] = ""
        elif current_title:
            ret[current_title] += chunk.strip()

    return ret

def construct_chunks_and_separators(chunk2separator):
    return "\n\n".join([f"### {k}\n{v}" for k, v in chunk2separator.items()])

def match_chunk_span_in_text(chunk, text):
    diff = difflib.Differ().compare(chunk, text)

    chunk_i = 0
    text_i = 0

    for tag in diff:
        if tag.startswith(' '):
            chunk_i += 1
            text_i += 1
        elif tag.startswith('+'):
            text_i += 1
        else:
            chunk_i += 1
        
        if chunk_i == 1:
            l = text_i - 1
        
        if chunk_i == len(chunk):
            r = text_i
            return l, r

def load_yaml(file_path):
    with open(file_path, 'r', encoding='utf-8') as file:  
        return yaml.safe_load(file)  

def load_text(file_path, read_size=None): 
    # Read the raw bytes first
    with open(file_path, 'rb') as file:
        raw_data = file.read(read_size)
    
    # Detect the encoding
    result = chardet.detect(raw_data[:10000])
    encoding = result['encoding'] or 'utf-8'  # Fallback to utf-8 if detection fails
    
    # Decode the content with detected encoding
    try:
        return raw_data.decode(encoding, errors='ignore')
    except UnicodeDecodeError:
        # Fallback to utf-8 if the detected encoding fails
        return raw_data.decode('utf-8', errors='ignore')

def load_jinja2_template(file_path):
    env = Environment(loader=FileSystemLoader(os.path.dirname(file_path)))
    template = env.get_template(os.path.basename(file_path)) 

    return template


